#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include "MonitorPisos.h"
#include "Constantes.h"
#include "Logger.h"
#include "Util.h"

#define PROCESS_NAME "MONITOR_PISOS"

using namespace std;

void MonitorPisos::log(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": " + message;
	logger->loggearLog(msg);
}

void MonitorPisos::sync(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": " + message;
	logger->loggearSynchro(msg);
}

void MonitorPisos::debug(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": " + message;
	logger->loggearDebug(msg);
}

MonitorPisos::MonitorPisos(int cantidadPisos) {

	this->logger = Logger::getInstance();
	this->cantidadPisos = cantidadPisos;
	this->crearMc();
	this->crearSemaforos();
}

int MonitorPisos::crearMc() {
	this->mcPisos = new MemoriaCompartida<int>();

	string message = "Total de memoria reservada por piso = ";
	message += Util::intToString(sizeof(int));
	this->sync(message);

	int estadoMemoria = this->mcPisos->crear((char*) MEM_PISOS, MEM_LETRA,
			sizeof(int), this->cantidadPisos * 2);
	if (estadoMemoria != SHM_OK) {
		this->sync("Error en creación SHM de personas por piso.");
		return ERROR_SHM;
	}
	this->sync("Fin de creación SHM de personas por piso.");
	return SUCCESS;
}

void MonitorPisos::crearSemaforos() {
	this->semPisos = (Semaforo**) malloc(
			sizeof(Semaforo*) * this->cantidadPisos);

	for (int i = 0; i < this->cantidadPisos; i++) {
		this->semPisos[i] = new Semaforo((char*) SEM_ARCHIVO_PIS, (char) i, 2);
	}
	this->sync("Semáforos por piso creados.");
}

void MonitorPisos::inicializar() {
	this->inicializarMc();
	this->inicializarSem();
}

void MonitorPisos::inicializarSem() {
	int valoresIniciales[2];
	valoresIniciales[SEM_UP_FLOOR] = 1;
	valoresIniciales[SEM_DOWN_FLOOR] = 1;
	for (int i = 0; i < this->cantidadPisos; i++)
		this->semPisos[i]->inicializar(valoresIniciales);
	this->sync("Semáforos por piso inicializados.");
}

void MonitorPisos::inicializarMc() {

	/* Convencion:
	            	this->mcPisos[2n] es para bajar
					this->mcPisos[2n+1] es para subir
	*/
	for (int i = 0; i < this->cantidadPisos * 2; i++) {
		this->mcPisos->escribirPos(0, i);
	}
	this->debug("Personas por piso inicializadas.");

}

void MonitorPisos::liberarSem() {
	for (int i = 0; i < this->cantidadPisos; i++) {
		this->semPisos[i]->eliminar_todos();
	}
	this->debug("Semáforos por piso liberados.");
}

void MonitorPisos::liberarMc() {
	this->mcPisos->liberar();
	this->debug("Memoria por piso liberada.");
}

void MonitorPisos::incrementar(int piso, const char estado) {

	int offset;
	string msg;
	if (estado == ESPERANDO_BAJAR) {
		offset = 2 * piso;
		this->sync(
				"P ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + estado + " en el conjunto 0");
		this->semPisos[piso]->p(0, 1);
		this->escribirMc(piso, 1, offset, SUMAR);
		this->sync(
				"V ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + estado + " en el conjunto 0");
		this->semPisos[piso]->v(0, 1);
	} else {
		offset = 2 * piso + 1;
		this->sync(
				"P ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + estado + " en el conjunto 1");
		this->semPisos[piso]->p(1, 1);
		this->escribirMc(piso, 1, offset, SUMAR);
		this->sync(
				"V ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + estado + " en el conjunto  1");
		this->semPisos[piso]->v(1, 1);
	}
}

int MonitorPisos::bloquearPiso(int piso, const char sentido) {
	int offset;
	int cantidad;
	if (sentido == BAJANDO) {
		offset = 2 * piso;
		this->sync(
				"P ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 0");
		this->semPisos[piso]->p(0, 1);
		this->sync("SHM Pisos leyendo el valor");
		cantidad = this->mcPisos->leerPos(offset);
	} else {
		offset = 2 * piso + 1;
		this->sync(
				"P ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 1");
		this->semPisos[piso]->p(1, 1);
		this->sync("SHM Pisos leyendo el valor");
		cantidad = this->mcPisos->leerPos(offset);
	}
	return cantidad;
}

void MonitorPisos::actualizar(int piso, const char sentido, int cantidad) {
	int offset;
	if (sentido == BAJANDO) {
		offset = 2 * piso;
		this->escribirMc(piso, cantidad, offset, RESTAR);
		this->sync(
				"V ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 0");
		this->semPisos[piso]->v(0, 1);
	} else {
		offset = 2 * piso + 1;
		this->escribirMc(piso, cantidad, offset, RESTAR);
		this->sync(
				"V ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 1");
		this->semPisos[piso]->v(1, 1);
	}
}

void MonitorPisos::escribirMc(int piso, int cantidad, int offset,
		const char op) {

	int cantAct = this->mcPisos->leerPos(offset);

	if (op == RESTAR) {
		cantAct -= cantidad;
	} else if (op == SUMAR) {
		cantAct += cantidad;
	} else {
		cantAct = cantidad;
	}

	this->sync(
			"Escribimos SHM PISOS para piso " + Util::intToString(piso)
					+ " la nueva cantidad " + Util::intToString(cantAct));
	this->mcPisos->escribirPos(cantAct, offset);
	this->sync(
			"Terminamos de escribir SHM PISOS para piso "
					+ Util::intToString(piso));
}

int MonitorPisos::leer(int piso, const char sentido) {
	int offset;
	int cantidad;
	if (sentido == BAJANDO) {
		offset = 2 * piso;
		this->sync(
				"P ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 0");
		this->semPisos[piso]->p(0, 1);

		cantidad = this->mcPisos->leerPos(offset);
		this->sync("SHM Pisos valor leido  " + Util::intToString(cantidad));
		this->sync(
				"V ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 0");
		this->semPisos[piso]->v(0, 1);
	} else {
		offset = 2 * piso + 1;
		this->sync(
				"P ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 1");
		this->semPisos[piso]->p(1, 1);

		cantidad = this->mcPisos->leerPos(offset);
		this->sync("SHM Pisos valor leido  " + Util::intToString(cantidad));
		this->sync(
				"V ---> Semáforos Piso " + Util::intToString(piso)
						+ " en el sentido " + sentido + " en el conjunto 1");
		this->semPisos[piso]->v(1, 1);
	}
	return cantidad;
}

MonitorPisos::~MonitorPisos() {
	// Semaforos
	for (int i = 0; i < this->cantidadPisos; i++) {
		delete (this->semPisos[i]);
	}
	free(this->semPisos);
	this->debug("Semáforos por piso destruidos.");

	// SHM
	this->liberarMc();;
	delete (this->mcPisos);
	this->debug("SHM personas por piso destruida.");
}
